﻿/**
 * SlotDuino
 * 
 * Copyright (c) 2011/2012 Thiago Hummel Moreira, Ricardo Mizuuti, Rafael Mufato, Bruno Sakai, Andre Turina
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using SlotDuino.DAL;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using SlotDuino.Sockets;
using SlotDuino.UI.Pages;

namespace SlotDuino.UI.Controls
{
    /// <summary>
    /// Interaction logic for FreeRaceControl.xaml
    /// </summary>
    public partial class FreeRacesControl : UserControl
    {
        #region Dependency properties
        public static readonly DependencyProperty ContextProperty = DependencyProperty.Register("Context", typeof(EntityContainer), typeof(FreeRacesControl));
        public static readonly DependencyProperty TrackProperty = DependencyProperty.Register("Track", typeof(Track), typeof(FreeRacesControl));
        public static readonly DependencyProperty ClientProperty = DependencyProperty.Register("Client", typeof(SlotDaemonClient), typeof(FreeRacesControl));
        #endregion

        private ObservableCollection<FreeRace> _raceList;

        #region Public properties
        public EntityContainer Context
        {
            get { return (EntityContainer)GetValue(ContextProperty); }
            set { SetValue(ContextProperty, value); }
        }
        
        public Track Track
        {
            get { return (Track)GetValue(TrackProperty); }
            set { SetValue(TrackProperty, value); }
        }

        public SlotDaemonClient Client
        {
            get { return (SlotDaemonClient)GetValue(ClientProperty); }
            set { SetValue(ClientProperty, value); }
        }
        #endregion

        public FreeRacesControl()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            //Create the raceList
            _raceList = new ObservableCollection<FreeRace>(GetCurrentRaces(Context));

            //Setup view sources
            var racesViewSource = ((CollectionViewSource)(FindResource("racesSource")));
            racesViewSource.Source = _raceList;

            Client.TrackUpdate += new System.EventHandler<CommandReceivedEventArgs>(OnTrackUpdate);
        }

        void OnTrackUpdate(object sender, CommandReceivedEventArgs e)
        {
            string[] parts = e.Command.Substring(e.Command.IndexOf(':') + 1).Split(';');

            int raceId = int.Parse(parts[1]);
            var race = _raceList.Single(r => r.Id == raceId);

            switch (parts[0])
            {
                //lapCompleted;RACE_ID;LANE_ID;PILOT_ID;LAP_NUMBER;LAP_TIME
                case "lapCompleted":
                    byte slotId = (byte)(byte.Parse(parts[2]) + 1);
                    int lapNumber = int.Parse(parts[4]);

                    var slot = race.Slots.Single(s => s.SlotId == slotId);
                    
                    Lap lap = new Lap();
                    lap.Number = lapNumber;
                    lap.Time = DateTimeOffset.Parse(parts[5]);
                    slot.Laps.Add(lap);

                    //var fastest = slot.Laps.Min(l => l.Time);
                    var card = GetSlotCard(slot.SlotId);
                    if (null != card)
                    {
                        Dispatcher.Invoke(new Action(() => {
                            card.LapCount = lapNumber;
                            card.LastLap = TimeSpan.Parse(parts[5]);
                            //card.BestLap = TimeSpan.Parse(fastest.ToString());
                        }));
                    }

                    UpdatePositions(race);
                    break;

                //raceEnded;RACE_ID;WHEN;WAS_ABORTED
                case "raceEnded":
                    race.State = (byte)RaceState.Completed;
                    race.Finish = DateTime.Now;
                    break;
            }

            Dispatcher.Invoke(new Action(() => { Context.SaveChanges(); }));
        }

        private IEnumerable<FreeRace> GetCurrentRaces(EntityContainer context)
        {
            return from r in context.Races.OfType<FreeRace>()
                   where r.State != (byte)RaceState.Closed
                   select r;
        }

        private void btnNewRace_Click(object sender, RoutedEventArgs e)
        {
            //Create a CreateRace object
            var raceCreate = new RaceCreate();
            raceCreate.Track = Track;
            //Set the avaliable pilots
            raceCreate.Pilots = Context.People.OfType<Pilot>().ToList();
            //Listen to RaceCreated event
            raceCreate.RaceCreated += new System.EventHandler(OnRaceCreated);
            //Listen to Cancel event
            raceCreate.Cancel += new System.EventHandler(OnCancel);

            //Insert it layoutRoot
            layoutRoot.Children.Add(raceCreate);

            //Disable the New Race button (will hide the race grid too)
            btnNewRace.IsEnabled = false;
        }

        private void OnRaceCreated(object sender, System.EventArgs e)
        {
            var raceCreate = (RaceCreate)sender;
            var race = raceCreate.Race;
            
            //Add the created race to the objects context
            Context.AddToRaces(race);
            //Persist the race on DB
            Context.SaveChanges();

            //Add pilots info to RaceDashboard
            foreach (RaceSlot slot in race.Slots)
            {
                var card = GetSlotCard(slot.SlotId);
                if (null != card)
                {
                    Dispatcher.Invoke(new Action(() => {
                        card.PilotName = slot.Pilot.Nickname;
                        card.Voltage = Track.Voltages.Single(v => v.Id == slot.Voltage).Label;
                    }));
                }
            }
            
            //Add to raceList
            _raceList.Add(race);

            //Create the race in SlotDaemon
            Client.CreateRace(race);

            //Close RaceCreate
            CloseRaceCreate(raceCreate);
        }

        private void OnCancel(object sender, System.EventArgs e)
        {
            //Close RaceCreate
            CloseRaceCreate((RaceCreate)sender);
        }

        private void CloseRaceCreate(RaceCreate raceCreate)
        {
            //Remove RaceCreate control
            layoutRoot.Children.Remove(raceCreate);

            //Enable NewRace button (and shows the racesGrid
            btnNewRace.IsEnabled = true;
        }

        private void btnStartRace_Click(object sender, RoutedEventArgs e)
        {
            var race = (FreeRace)dtgOngoingRaces.SelectedItem;
            race.State = (byte)RaceState.Running;
            race.Start = DateTime.Now;
            Context.SaveChanges();
            
            Client.StartRace(race);
        }

        private void btnCloseRace_Click(object sender, RoutedEventArgs e)
        {
            var race = (FreeRace)dtgOngoingRaces.SelectedItem;
            race.State = (byte)RaceState.Closed;
            Context.SaveChanges();

            //Remove pilots info to RaceDashboard
            foreach (RaceSlot slot in race.Slots)
            {
                var card = GetSlotCard(slot.SlotId);
                if (null != card)
                {
                    Dispatcher.Invoke(new Action(() => {
                        card.PilotName = "";
                        card.Voltage = "0V";
                        card.LapCount = 0;
                        card.LastLap = TimeSpan.Zero;
                        card.BestLap = TimeSpan.Zero;
                        card.Position = 0;
                    }));
                }
            }
            
            _raceList.Remove(race);

            Client.CloseRace(race);
        }

        private void btnPauseRaces_Click(object sender, RoutedEventArgs e)
        {
            Client.PauseRaces();

            //Pause current running races
            foreach (FreeRace race in _raceList)
            {
                if (RaceState.Running == (RaceState)race.State)
                    race.State = (byte)RaceState.Paused;
            }
            Context.SaveChanges();
        }

        private void btnResumeRaces_Click(object sender, RoutedEventArgs e)
        {
            Client.ResumeRaces();

            //Resume current paused races
            foreach (FreeRace race in _raceList)
            {
                if (RaceState.Paused == (RaceState)race.State)
                    race.State = (byte)RaceState.Running;
            }
            //Persist changes on DB
            Context.SaveChanges();
        }

        private SlotCard GetSlotCard(byte slotId)
        {
            var raceDashboard = (RaceDashboard)((Grid)Parent).Parent;
            return raceDashboard.SlotCards[slotId];
        }

        private void UpdatePositions(Race race)
        {
            var positions = race.Slots.OrderByDescending(s => s.Laps.Count);
            byte pos = 0; 
            foreach (RaceSlot slot in positions)
            {
                var card = GetSlotCard(slot.SlotId);
                Dispatcher.Invoke(new Action(() => { card.Position = ++pos; }));
            }
        }
    }
}
